Step 0: check and install needed packages. Load the libraries and functions.
This notebook was prepared with the following environmental settings.
Following the example of Jerid Francom, we used Selectorgadget to choose the links we would like to scrap. For this project, we selected all inaugural addresses of past presidents, nomination speeches of major party candidates and farewell addresses. We also included several public speeches from Donald Trump for our textual analysis of presidential speeches.
### Inauguaral speeches
main.page <- read_html(x = "http://www.presidency.ucsb.edu/inaugurals.php")
# Get link URLs
# f.speechlinks is a function for extracting links from the list of speeches.
inaug=f.speechlinks(main.page)
#head(inaug)
#as.Date(inaug[,1], format="%B %e, %Y")
inaug=inaug[-nrow(inaug),] # remove the last line, irrelevant due to error.
#### Nomination speeches
main.page=read_html("http://www.presidency.ucsb.edu/nomination.php")
# Get link URLs
nomin <- f.speechlinks(main.page)
#head(nomin)
#
#### Farewell speeches
main.page=read_html("http://www.presidency.ucsb.edu/farewell_addresses.php")
# Get link URLs
farewell <- f.speechlinks(main.page)
#head(farewell)
Step 2: Using speech metadata posted on http://www.presidency.ucsb.edu/, we prepared CSV data sets for the speeches we will scrap.
inaug.list=read.csv("../data/inauglist.csv", stringsAsFactors = FALSE)
nomin.list=read.csv("../data/nominlist.csv", stringsAsFactors = FALSE)
farewell.list=read.csv("../data/farewelllist.csv", stringsAsFactors = FALSE)
We assemble all scrapped speeches into one list. Note here that we don’t have the full text yet, only the links to full text transcripts.
Step 3: scrap the texts of speeches from the speech URLs.
speech.list=rbind(inaug.list, nomin.list, farewell.list)
speech.list$type=c(rep("inaug", nrow(inaug.list)),
rep("nomin", nrow(nomin.list)),
rep("farewell", nrow(farewell.list)))
speech.url=rbind(inaug, nomin, farewell)
speech.list=cbind(speech.list, speech.url)
Based on the list of speeches, we scrap the main text part of the transcript’s html page. For simple html pages of this kind, Selectorgadget is very convenient for identifying the html node that rvest can use to scrap its content. For reproducibility, we also save our scrapped speeches into our local folder as individual speech files.
# Loop over each row in speech.list
speech.list$fulltext=NA
for(i in seq(nrow(speech.list))) {
text <- read_html(speech.list$urls[i]) %>% # load the page
html_nodes(".displaytext") %>% # isloate the text
html_text() # get the text
speech.list$fulltext[i]=text
# Create the file name
filename <- paste0("../data/fulltext/",
speech.list$type[i],
speech.list$File[i], "-",
speech.list$Term[i], ".txt")
sink(file = filename) %>% # open file to write
cat(text) # write the file
sink() # close the file
}
Trump, as president-elect that has not been a politician, do not have a lot of formal speeches yet. For our textual analysis, we manually add several public transcripts from Trump: + [Transcript: Donald Trump’s full immigration speech, annotated. LA Times, 08/31/2016] (http://www.latimes.com/politics/la-na-pol-donald-trump-immigration-speech-transcript-20160831-snap-htmlstory.html) + Transcript of Donald Trump’s speech on national security in Philadelphia - The Hill, 09/07/16 + Transcript of President-elect Trump’s news conference CNBC, 01/11/2017
speech1=paste(readLines("../data/fulltext/SpeechDonaldTrump-NA.txt",
n=-1, skipNul=TRUE),
collapse=" ")
speech2=paste(readLines("../data/fulltext/SpeechDonaldTrump-NA2.txt",
n=-1, skipNul=TRUE),
collapse=" ")
speech3=paste(readLines("../data/fulltext/PressDonaldTrump-NA.txt",
n=-1, skipNul=TRUE),
collapse=" ")
Trump.speeches=data.frame(
President=rep("Donald J. Trump", 3),
File=rep("DonaldJTrump", 3),
Term=rep(0, 3),
Party=rep("Republican", 3),
Date=c("August 31, 2016", "September 7, 2016", "January 11, 2017"),
Words=c(word_count(speech1), word_count(speech2), word_count(speech3)),
Win=rep("yes", 3),
type=rep("speeches", 3),
links=rep(NA, 3),
urls=rep(NA, 3),
fulltext=c(speech1, speech2, speech3)
)
speech.list=rbind(speech.list, Trump.speeches)
Step 4: data Processing — generate list of sentences
We will use sentences as units of analysis for this project, as sentences are natural languge units for organizing thoughts and ideas. For each extracted sentence, we apply sentiment analysis using NRC sentiment lexion. “The NRC Emotion Lexicon is a list of English words and their associations with eight basic emotions (anger, fear, anticipation, trust, surprise, sadness, joy, and disgust) and two sentiments (negative and positive). The annotations were manually done by crowdsourcing.”
We assign an sequential id to each sentence in a speech (sent.id) and also calculated the number of words in each sentence as sentence length (word.count).
sentence.list=NULL
for(i in 1:nrow(speech.list)){
sentences=sent_detect(speech.list$fulltext[i],
endmarks = c("?", ".", "!", "|",";"))
if(length(sentences)>0){
emotions=get_nrc_sentiment(sentences)
word.count=word_count(sentences)
# colnames(emotions)=paste0("emo.", colnames(emotions))
# in case the word counts are zeros?
emotions=diag(1/(word.count+0.01))%*%as.matrix(emotions)
sentence.list=rbind(sentence.list,
cbind(speech.list[i,-ncol(speech.list)],
sentences=as.character(sentences),
word.count,
emotions,
sent.id=1:length(sentences)
)
)
}
}
Some non-sentences exist in raw data due to erroneous extra end-of sentence marks.
sentence.list=
sentence.list%>%
filter(!is.na(word.count))
Step 5: Data analysis — length of sentences
For simpler visualization, we chose a subset of better known presidents or presidential candidates on which to focus our analysis.
sel.comparison <- c()
for(i in 1:length(unique(speech.list$President))){
sel.comparison[i] <- unique(speech.list$File)[i]
}
sel.comparison <- sel.comparison[-c(59:61)]
Overview of sentence length distribution by different types of speeches.
Nomination speeches
First, we look at nomination acceptance speeches at major party’s national conventions. For relevant to Trump’s speeches, we limit our attention to speeches for the first terms of former U.S. presidents. We noticed that a number of presidents have very short sentences in their nomination acceptance speeches.
First term
par(mar=c(4, 11, 2, 2))
#sel.comparison=levels(sentence.list$FileOrdered)
sentence.list.sel=filter(sentence.list,
File == "DonaldJTrump")
sentence.list.sel$type=factor(sentence.list.sel$type)
sentence.list.sel$typeOrdered=reorder(sentence.list.sel$type,
sentence.list.sel$word.count,
mean,
order=T)
beeswarm(word.count~typeOrdered,
data=sentence.list.sel,
horizontal = TRUE,
pch=16, col=alpha(brewer.pal(9, "Set1"), 0.6),
cex=0.55, cex.axis=0.8, cex.lab=0.8,
spacing=0.5/nlevels(sentence.list.sel$typeOrdered),
las=2, xlab="Number of words in a sentence.", ylab="",
main="Different speeches of DonaldJTrump")

sentence.list%>%
filter(File=="DonaldJTrump",
type=="nomin",
word.count<=3)%>%
select(sentences)%>%sample_n(10)
sentence.list%>%
filter(File=="DonaldJTrump",
type=="inaug",
word.count<=3)%>%
select(sentences)%>%sample_n(5)
sentence.list%>%
filter(File=="DonaldJTrump",
type=="speeches",
word.count<=3)%>%
select(sentences)%>%sample_n(10)
Second term
count_term <- table(speech.list[speech.list$type == "inaug",]$File)
multi.term.sel <- names(which(count_term > 1))
for(i in 1:length(multi.term.sel)){
png(paste("../output/", multi.term.sel[i], "compare12.png", sep=""),
width=300, height=300)
#sel.comparison=levels(sentence.list$FileOrdered)
sentence.list.sel=filter(sentence.list,
type=="inaug", File%in%multi.term.sel[i])
sentence.list.sel$Term=factor(sentence.list.sel$Term)
sentence.list.sel$termOrdered=reorder(sentence.list.sel$Term,
sentence.list.sel$word.count,
mean,
order=T)
beeswarm(word.count~termOrdered,
data=sentence.list.sel,
horizontal = TRUE,
pch=16, col=alpha(brewer.pal(9, "Set1"), 0.6),
cex=0.55, cex.axis=0.8, cex.lab=0.8,
spacing=3/nlevels(sentence.list.sel$termOrdered),
las=2, xlab="Number of words in a sentence.", ylab="",
main="Inauguration speeches of multi-term")
dev.off()
}
What are these short sentences?
sentence.list%>%
filter(File=="DonaldJTrump",
type=="nomin",
word.count<=3)%>%
select(sentences)%>%sample_n(10)
sentence.list%>%
filter(File=="AlbertGore,Jr",
type=="nomin",
word.count<=3)%>%
select(sentences)%>%sample_n(10)
sentence.list%>%
filter(File=="Clinton",
type=="nomin",
word.count<=3)%>%
select(sentences)
sentence.list%>%
filter(File=="WilliamJClinton",
type=="nomin", Term==1,
word.count<=3)%>%
select(sentences)
Inaugural speeches
We notice that the sentences in inaugural speeches are longer than those in nomination acceptance speeches.
sentence.list.sel=sentence.list%>%filter(type=="inaug", File%in%sel.comparison, Term==1)
sentence.list.sel$File=factor(sentence.list.sel$File)
sentence.list.sel$FileOrdered=reorder(sentence.list.sel$File,
sentence.list.sel$word.count,
mean,
order=T)
par(mar=c(4, 11, 2, 2))
beeswarm(word.count~FileOrdered,
data=sentence.list.sel,
horizontal = TRUE,
pch=16, col=alpha(brewer.pal(9, "Set1"), 0.6),
cex=0.55, cex.axis=0.8, cex.lab=0.8,
spacing=5/nlevels(sentence.list.sel$FileOrdered),
las=2, ylab="", xlab="Number of words in a sentence.",
main="Inaugural Speeches")

Short sentences in inaugural speeches.
sentence.list%>%
filter(File=="BarackObama",
type=="inaug",
word.count<=3)%>%
select(sentences)
trump_bigrams <- speech.list[speech.list$File == "DonaldJTrump",] %>%
unnest_tokens(bigram, fulltext, token = "ngrams", n = 2)
bigrams_separated <- trump_bigrams %>%
separate(bigram, c("word1", "word2"), sep = " ")
bigrams_filtered <- bigrams_separated %>%
filter(!word1 %in% stop_words$word) %>%
filter(!word2 %in% stop_words$word)
bigram_counts <- bigrams_filtered %>%
count(word1, word2, sort = TRUE)
bigrams_united <- bigrams_filtered %>%
unite(bigram, word1, word2, sep = " ")
bigrams_united <- bigrams_united %>%
count(type, bigram) %>%
arrange(desc(n))
bigrams_united <- bigrams_united %>%
mutate(bigram = factor(bigram, levels = rev(unique(bigram)))) %>%
group_by(type) %>%
arrange(desc(n))%>%
top_n(5) %>%
ungroup
Selecting by n
ggplot(bigrams_united, aes(bigram, n, fill = type)) +
geom_bar(stat = "identity", show.legend = FALSE) +
labs(x = NULL, y = "count of words") +
facet_wrap(~type, ncol = 2, scales = "free") +
coord_flip()

##################
Step 5: Data analysis — sentiment analsis
Sentence length variation over the course of the speech, with emotions.
How our presidents (or candidates) alternate between long and short sentences and how they shift between different sentiments in their speeches. It is interesting to note that some presidential candidates’ speech are more colorful than others. Here we used the same color theme as in the movie “Inside Out.”
par(mfrow=c(4,1), mar=c(1,0,2,0), bty="n", xaxt="n", yaxt="n", font.main=1)
f.plotsent.len(In.list=sentence.list, InFile="HillaryClinton",
InType="nomin", InTerm=1, President="Hillary Clinton")
f.plotsent.len(In.list=sentence.list, InFile="DonaldJTrump",
InType="nomin", InTerm=1, President="Donald Trump")
f.plotsent.len(In.list=sentence.list, InFile="BarackObama",
InType="nomin", InTerm=1, President="Barack Obama")
f.plotsent.len(In.list=sentence.list, InFile="GeorgeWBush",
InType="nomin", InTerm=1, President="George W. Bush")

What are the emotionally charged sentences?
print("Hillary Clinton")
[1] "Hillary Clinton"
speech.df=tbl_df(sentence.list)%>%
filter(File=="HillaryClinton", type=="nomin", word.count>=4)%>%
select(sentences, anger:trust)
speech.df=as.data.frame(speech.df)
as.character(speech.df$sentences[apply(speech.df[,-1], 2, which.max)])
[1] "Some of you are frustrated, even furious."
[2] "It's a big deal."
[3] "Powerful forces are threatening to pull us apart."
[4] "Powerful forces are threatening to pull us apart."
[5] "It's a big deal."
[6] "My mother, Dorothy, was abandoned by her parents as a young girl."
[7] "It's a big deal."
[8] "Bonds of trust and respect are fraying."
print("Barack Obama")
[1] "Barack Obama"
speech.df=tbl_df(sentence.list)%>%
filter(File=="BarackObama", type=="nomin", Term==1, word.count>=5)%>%
select(sentences, anger:trust)
speech.df=as.data.frame(speech.df)
as.character(speech.df$sentences[apply(speech.df[,-1], 2, which.max)])
[1] "They could've heard words of anger and discord."
[2] "And that's to be expected."
[3] "It's not because John McCain doesn't care."
[4] "Now let there be no doubt."
[5] "That promise is our greatest inheritance."
[6] "Now let there be no doubt."
[7] "That's not the judgment we need."
[8] "That promise is our greatest inheritance."
print("George W Bush")
[1] "George W Bush"
speech.df=tbl_df(sentence.list)%>%
filter(File=="GeorgeWBush", type=="nomin", Term==1, word.count>=4)%>%
select(sentences, anger:trust)
speech.df=as.data.frame(speech.df)
as.character(speech.df$sentences[apply(speech.df[,-1], 2, which.max)])
[1] "On the other side of that wall are poverty and prison, addiction and despair."
[2] "We're proud of you."
[3] "But they've got it backwards."
[4] "And at the earliest possible date, my administration will deploy missile defenses to guard against attack and blackmail."
[5] "I appreciate his friendship."
[6] "On the other side of that wall are poverty and prison, addiction and despair."
[7] "They had their chance."
[8] "Corporations are responsible to treat their workers fairly and to leave the air and waters clean."
print("Donald Trump")
[1] "Donald Trump"
speech.df=tbl_df(sentence.list)%>%
filter(File=="DonaldJTrump", type=="nomin", Term==1, word.count>=5)%>%
select(sentences, anger:trust)
speech.df=as.data.frame(speech.df)
as.character(speech.df$sentences[apply(speech.df[,-1], 2, which.max)])
[1] "Once again, France is the victim of brutal Islamic terrorism."
[2] "God bless you, and good night!"
[3] "I have visited the laid-off factory workers, and the communities crushed by our horrible and unfair trade deals."
[4] "Once again, France is the victim of brutal Islamic terrorism."
[5] "God bless you, and good night!"
[6] "Three were killed, and three were very very badly injured."
[7] "God bless you, and good night!"
[8] "God bless you, and good night!"
Clustering of emotions
heatmap.2(cor(sentence.list%>%filter(type=="inaug")%>%select(anger:trust)),
scale = "none",
col = bluered(100), , margin=c(6, 6), key=F,
trace = "none", density.info = "none")
par(mar=c(4, 6, 2, 1))

emo.means=colMeans(select(sentence.list, anger:trust)>0.01)
col.use=c("red2", "darkgoldenrod1",
"chartreuse3", "blueviolet",
"darkgoldenrod2", "dodgerblue3",
"darkgoldenrod1", "darkgoldenrod1")
barplot(emo.means[order(emo.means)], las=2, col=col.use[order(emo.means)], horiz=T, main="Inaugural Speeches")

LS0tCnRpdGxlOiAiUiBOb3RlYm9vayIKb3V0cHV0OgogIGh0bWxfZG9jdW1lbnQ6IGRlZmF1bHQKICBodG1sX25vdGVib29rOiBkZWZhdWx0Ci0tLQoKCiMgU3RlcCAwOiBjaGVjayBhbmQgaW5zdGFsbCBuZWVkZWQgcGFja2FnZXMuIExvYWQgdGhlIGxpYnJhcmllcyBhbmQgZnVuY3Rpb25zLiAKCmBgYHtyLCBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFLCBpbmNsdWRlPSBGQUxTRX0Kc291cmNlKCIuLi9saWIvbGlicmFyeS5SIikKYGBgClRoaXMgbm90ZWJvb2sgd2FzIHByZXBhcmVkIHdpdGggdGhlIGZvbGxvd2luZyBlbnZpcm9ubWVudGFsIHNldHRpbmdzLgoKYGBge3J9CnByaW50KFIudmVyc2lvbikKYGBgCgojIFN0ZXAgMTogRGF0YSBoYXJ2ZXN0OiBzY3JhcCBzcGVlY2ggVVJMcyBmcm9tIDxodHRwOi8vd3d3LnByZXNpZGVuY3kudWNzYi5lZHUvPi4KCkZvbGxvd2luZyB0aGUgZXhhbXBsZSBvZiBbSmVyaWQgRnJhbmNvbV0oaHR0cDovL2ZyYW5jb2pjLmdpdGh1Yi5pby93ZWItc2NyYXBpbmctd2l0aC1ydmVzdC8pLCB3ZSB1c2VkIFtTZWxlY3RvcmdhZGdldF0oaHR0cDovL3NlbGVjdG9yZ2FkZ2V0LmNvbS8pIHRvIGNob29zZSB0aGUgbGlua3Mgd2Ugd291bGQgbGlrZSB0byBzY3JhcC4gRm9yIHRoaXMgcHJvamVjdCwgd2Ugc2VsZWN0ZWQgYWxsIGluYXVndXJhbCBhZGRyZXNzZXMgb2YgcGFzdCBwcmVzaWRlbnRzLCBub21pbmF0aW9uIHNwZWVjaGVzIG9mIG1ham9yIHBhcnR5IGNhbmRpZGF0ZXMgYW5kIGZhcmV3ZWxsIGFkZHJlc3Nlcy4gV2UgYWxzbyBpbmNsdWRlZCBzZXZlcmFsIHB1YmxpYyBzcGVlY2hlcyBmcm9tIERvbmFsZCBUcnVtcCBmb3Igb3VyIHRleHR1YWwgYW5hbHlzaXMgb2YgcHJlc2lkZW50aWFsIHNwZWVjaGVzLiAKCmBgYHtyLCBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFfQojIyMgSW5hdWd1YXJhbCBzcGVlY2hlcwptYWluLnBhZ2UgPC0gcmVhZF9odG1sKHggPSAiaHR0cDovL3d3dy5wcmVzaWRlbmN5LnVjc2IuZWR1L2luYXVndXJhbHMucGhwIikKIyBHZXQgbGluayBVUkxzCiMgZi5zcGVlY2hsaW5rcyBpcyBhIGZ1bmN0aW9uIGZvciBleHRyYWN0aW5nIGxpbmtzIGZyb20gdGhlIGxpc3Qgb2Ygc3BlZWNoZXMuIAppbmF1Zz1mLnNwZWVjaGxpbmtzKG1haW4ucGFnZSkKI2hlYWQoaW5hdWcpCiNhcy5EYXRlKGluYXVnWywxXSwgZm9ybWF0PSIlQiAlZSwgJVkiKQppbmF1Zz1pbmF1Z1stbnJvdyhpbmF1ZyksXSAjIHJlbW92ZSB0aGUgbGFzdCBsaW5lLCBpcnJlbGV2YW50IGR1ZSB0byBlcnJvci4KCiMjIyMgTm9taW5hdGlvbiBzcGVlY2hlcwptYWluLnBhZ2U9cmVhZF9odG1sKCJodHRwOi8vd3d3LnByZXNpZGVuY3kudWNzYi5lZHUvbm9taW5hdGlvbi5waHAiKQojIEdldCBsaW5rIFVSTHMKbm9taW4gPC0gZi5zcGVlY2hsaW5rcyhtYWluLnBhZ2UpCiNoZWFkKG5vbWluKQojCiMjIyMgRmFyZXdlbGwgc3BlZWNoZXMKbWFpbi5wYWdlPXJlYWRfaHRtbCgiaHR0cDovL3d3dy5wcmVzaWRlbmN5LnVjc2IuZWR1L2ZhcmV3ZWxsX2FkZHJlc3Nlcy5waHAiKQojIEdldCBsaW5rIFVSTHMKZmFyZXdlbGwgPC0gZi5zcGVlY2hsaW5rcyhtYWluLnBhZ2UpCiNoZWFkKGZhcmV3ZWxsKQpgYGAKCiMgU3RlcCAyOiBVc2luZyBzcGVlY2ggbWV0YWRhdGEgcG9zdGVkIG9uIDxodHRwOi8vd3d3LnByZXNpZGVuY3kudWNzYi5lZHUvPiwgd2UgcHJlcGFyZWQgQ1NWIGRhdGEgc2V0cyBmb3IgdGhlIHNwZWVjaGVzIHdlIHdpbGwgc2NyYXAuIAoKYGBge3J9CmluYXVnLmxpc3Q9cmVhZC5jc3YoIi4uL2RhdGEvaW5hdWdsaXN0LmNzdiIsIHN0cmluZ3NBc0ZhY3RvcnMgPSBGQUxTRSkKbm9taW4ubGlzdD1yZWFkLmNzdigiLi4vZGF0YS9ub21pbmxpc3QuY3N2Iiwgc3RyaW5nc0FzRmFjdG9ycyA9IEZBTFNFKQpmYXJld2VsbC5saXN0PXJlYWQuY3N2KCIuLi9kYXRhL2ZhcmV3ZWxsbGlzdC5jc3YiLCBzdHJpbmdzQXNGYWN0b3JzID0gRkFMU0UpCmBgYAoKV2UgYXNzZW1ibGUgYWxsIHNjcmFwcGVkIHNwZWVjaGVzIGludG8gb25lIGxpc3QuIE5vdGUgaGVyZSB0aGF0IHdlIGRvbid0IGhhdmUgdGhlIGZ1bGwgdGV4dCB5ZXQsIG9ubHkgdGhlIGxpbmtzIHRvIGZ1bGwgdGV4dCB0cmFuc2NyaXB0cy4gCgojIFN0ZXAgMzogc2NyYXAgdGhlIHRleHRzIG9mIHNwZWVjaGVzIGZyb20gdGhlIHNwZWVjaCBVUkxzLgoKYGBge3J9CnNwZWVjaC5saXN0PXJiaW5kKGluYXVnLmxpc3QsIG5vbWluLmxpc3QsIGZhcmV3ZWxsLmxpc3QpCnNwZWVjaC5saXN0JHR5cGU9YyhyZXAoImluYXVnIiwgbnJvdyhpbmF1Zy5saXN0KSksCiAgICAgICAgICAgICAgICAgICByZXAoIm5vbWluIiwgbnJvdyhub21pbi5saXN0KSksCiAgICAgICAgICAgICAgICAgICByZXAoImZhcmV3ZWxsIiwgbnJvdyhmYXJld2VsbC5saXN0KSkpCnNwZWVjaC51cmw9cmJpbmQoaW5hdWcsIG5vbWluLCBmYXJld2VsbCkKc3BlZWNoLmxpc3Q9Y2JpbmQoc3BlZWNoLmxpc3QsIHNwZWVjaC51cmwpCmBgYAoKQmFzZWQgb24gdGhlIGxpc3Qgb2Ygc3BlZWNoZXMsIHdlIHNjcmFwIHRoZSBtYWluIHRleHQgcGFydCBvZiB0aGUgdHJhbnNjcmlwdCdzIGh0bWwgcGFnZS4gRm9yIHNpbXBsZSBodG1sIHBhZ2VzIG9mIHRoaXMga2luZCwgIFtTZWxlY3RvcmdhZGdldF0oaHR0cDovL3NlbGVjdG9yZ2FkZ2V0LmNvbS8pIGlzIHZlcnkgY29udmVuaWVudCBmb3IgaWRlbnRpZnlpbmcgdGhlIGh0bWwgbm9kZSB0aGF0IGBydmVzdGAgY2FuIHVzZSB0byBzY3JhcCBpdHMgY29udGVudC4gRm9yIHJlcHJvZHVjaWJpbGl0eSwgd2UgYWxzbyBzYXZlIG91ciBzY3JhcHBlZCBzcGVlY2hlcyBpbnRvIG91ciBsb2NhbCBmb2xkZXIgYXMgaW5kaXZpZHVhbCBzcGVlY2ggZmlsZXMuIAoKYGBge3J9CiMgTG9vcCBvdmVyIGVhY2ggcm93IGluIHNwZWVjaC5saXN0CnNwZWVjaC5saXN0JGZ1bGx0ZXh0PU5BCmZvcihpIGluIHNlcShucm93KHNwZWVjaC5saXN0KSkpIHsKICB0ZXh0IDwtIHJlYWRfaHRtbChzcGVlY2gubGlzdCR1cmxzW2ldKSAlPiUgIyBsb2FkIHRoZSBwYWdlCiAgICBodG1sX25vZGVzKCIuZGlzcGxheXRleHQiKSAlPiUgIyBpc2xvYXRlIHRoZSB0ZXh0CiAgICBodG1sX3RleHQoKSAjIGdldCB0aGUgdGV4dAogIHNwZWVjaC5saXN0JGZ1bGx0ZXh0W2ldPXRleHQKICAjIENyZWF0ZSB0aGUgZmlsZSBuYW1lCiAgZmlsZW5hbWUgPC0gcGFzdGUwKCIuLi9kYXRhL2Z1bGx0ZXh0LyIsIAogICAgICAgICAgICAgICAgICAgICBzcGVlY2gubGlzdCR0eXBlW2ldLAogICAgICAgICAgICAgICAgICAgICBzcGVlY2gubGlzdCRGaWxlW2ldLCAiLSIsIAogICAgICAgICAgICAgICAgICAgICBzcGVlY2gubGlzdCRUZXJtW2ldLCAiLnR4dCIpCiAgc2luayhmaWxlID0gZmlsZW5hbWUpICU+JSAjIG9wZW4gZmlsZSB0byB3cml0ZSAKICBjYXQodGV4dCkgICMgd3JpdGUgdGhlIGZpbGUKICBzaW5rKCkgIyBjbG9zZSB0aGUgZmlsZQp9CmBgYAoKVHJ1bXAsIGFzIHByZXNpZGVudC1lbGVjdCB0aGF0IGhhcyBub3QgYmVlbiBhIHBvbGl0aWNpYW4sIGRvIG5vdCBoYXZlIGEgbG90IG9mIGZvcm1hbCBzcGVlY2hlcyB5ZXQuIEZvciBvdXIgdGV4dHVhbCBhbmFseXNpcywgd2UgbWFudWFsbHkgYWRkIHNldmVyYWwgcHVibGljIHRyYW5zY3JpcHRzIGZyb20gVHJ1bXA6CisgW1RyYW5zY3JpcHQ6IERvbmFsZCBUcnVtcCdzIGZ1bGwgaW1taWdyYXRpb24gc3BlZWNoLCBhbm5vdGF0ZWQuIExBIFRpbWVzLCAwOC8zMS8yMDE2XSAoaHR0cDovL3d3dy5sYXRpbWVzLmNvbS9wb2xpdGljcy9sYS1uYS1wb2wtZG9uYWxkLXRydW1wLWltbWlncmF0aW9uLXNwZWVjaC10cmFuc2NyaXB0LTIwMTYwODMxLXNuYXAtaHRtbHN0b3J5Lmh0bWwpCisgW1RyYW5zY3JpcHQgb2YgRG9uYWxkIFRydW1w4oCZcyBzcGVlY2ggb24gbmF0aW9uYWwgc2VjdXJpdHkgaW4gUGhpbGFkZWxwaGlhCi0gVGhlIEhpbGwsIDA5LzA3LzE2XShodHRwOi8vdGhlaGlsbC5jb20vYmxvZ3MvcHVuZGl0cy1ibG9nL2NhbXBhaWduLzI5NDgxNy10cmFuc2NyaXB0LW9mLWRvbmFsZC10cnVtcHMtc3BlZWNoLW9uLW5hdGlvbmFsLXNlY3VyaXR5LWluKQorIFtUcmFuc2NyaXB0IG9mIFByZXNpZGVudC1lbGVjdCBUcnVtcCdzIG5ld3MgY29uZmVyZW5jZQpDTkJDLCAwMS8xMS8yMDE3XShodHRwOi8vd3d3LmNuYmMuY29tLzIwMTcvMDEvMTEvdHJhbnNjcmlwdC1vZi1wcmVzaWRlbnQtZWxlY3QtZG9uYWxkLWotdHJ1bXBzLW5ld3MtY29uZmVyZW5jZS5odG1sKQoKYGBge3J9CnNwZWVjaDE9cGFzdGUocmVhZExpbmVzKCIuLi9kYXRhL2Z1bGx0ZXh0L1NwZWVjaERvbmFsZFRydW1wLU5BLnR4dCIsIAogICAgICAgICAgICAgICAgICBuPS0xLCBza2lwTnVsPVRSVUUpLAogICAgICAgICAgICAgIGNvbGxhcHNlPSIgIikKc3BlZWNoMj1wYXN0ZShyZWFkTGluZXMoIi4uL2RhdGEvZnVsbHRleHQvU3BlZWNoRG9uYWxkVHJ1bXAtTkEyLnR4dCIsIAogICAgICAgICAgICAgICAgICBuPS0xLCBza2lwTnVsPVRSVUUpLAogICAgICAgICAgICAgIGNvbGxhcHNlPSIgIikKc3BlZWNoMz1wYXN0ZShyZWFkTGluZXMoIi4uL2RhdGEvZnVsbHRleHQvUHJlc3NEb25hbGRUcnVtcC1OQS50eHQiLCAKICAgICAgICAgICAgICAgICAgbj0tMSwgc2tpcE51bD1UUlVFKSwKICAgICAgICAgICAgICBjb2xsYXBzZT0iICIpCgpUcnVtcC5zcGVlY2hlcz1kYXRhLmZyYW1lKAogIFByZXNpZGVudD1yZXAoIkRvbmFsZCBKLiBUcnVtcCIsIDMpLAogIEZpbGU9cmVwKCJEb25hbGRKVHJ1bXAiLCAzKSwKICBUZXJtPXJlcCgwLCAzKSwKICBQYXJ0eT1yZXAoIlJlcHVibGljYW4iLCAzKSwKICBEYXRlPWMoIkF1Z3VzdCAzMSwgMjAxNiIsICJTZXB0ZW1iZXIgNywgMjAxNiIsICJKYW51YXJ5IDExLCAyMDE3IiksCiAgV29yZHM9Yyh3b3JkX2NvdW50KHNwZWVjaDEpLCB3b3JkX2NvdW50KHNwZWVjaDIpLCB3b3JkX2NvdW50KHNwZWVjaDMpKSwKICBXaW49cmVwKCJ5ZXMiLCAzKSwKICB0eXBlPXJlcCgic3BlZWNoZXMiLCAzKSwKICBsaW5rcz1yZXAoTkEsIDMpLAogIHVybHM9cmVwKE5BLCAzKSwKICBmdWxsdGV4dD1jKHNwZWVjaDEsIHNwZWVjaDIsIHNwZWVjaDMpCikKCnNwZWVjaC5saXN0PXJiaW5kKHNwZWVjaC5saXN0LCBUcnVtcC5zcGVlY2hlcykKYGBgCgojIFN0ZXAgNDogZGF0YSBQcm9jZXNzaW5nIC0tLSBnZW5lcmF0ZSBsaXN0IG9mIHNlbnRlbmNlcwoKV2Ugd2lsbCB1c2Ugc2VudGVuY2VzIGFzIHVuaXRzIG9mIGFuYWx5c2lzIGZvciB0aGlzIHByb2plY3QsIGFzIHNlbnRlbmNlcyBhcmUgbmF0dXJhbCBsYW5ndWdlIHVuaXRzIGZvciBvcmdhbml6aW5nIHRob3VnaHRzIGFuZCBpZGVhcy4gRm9yIGVhY2ggZXh0cmFjdGVkIHNlbnRlbmNlLCB3ZSBhcHBseSBzZW50aW1lbnQgYW5hbHlzaXMgdXNpbmcgW05SQyBzZW50aW1lbnQgbGV4aW9uXShodHRwOi8vc2FpZm1vaGFtbWFkLmNvbS9XZWJQYWdlcy9OUkMtRW1vdGlvbi1MZXhpY29uLmh0bSkuICJUaGUgTlJDIEVtb3Rpb24gTGV4aWNvbiBpcyBhIGxpc3Qgb2YgRW5nbGlzaCB3b3JkcyBhbmQgdGhlaXIgYXNzb2NpYXRpb25zIHdpdGggZWlnaHQgYmFzaWMgZW1vdGlvbnMgKGFuZ2VyLCBmZWFyLCBhbnRpY2lwYXRpb24sIHRydXN0LCBzdXJwcmlzZSwgc2FkbmVzcywgam95LCBhbmQgZGlzZ3VzdCkgYW5kIHR3byBzZW50aW1lbnRzIChuZWdhdGl2ZSBhbmQgcG9zaXRpdmUpLiBUaGUgYW5ub3RhdGlvbnMgd2VyZSBtYW51YWxseSBkb25lIGJ5IGNyb3dkc291cmNpbmcuIgoKV2UgYXNzaWduIGFuIHNlcXVlbnRpYWwgaWQgdG8gZWFjaCBzZW50ZW5jZSBpbiBhIHNwZWVjaCAoYHNlbnQuaWRgKSBhbmQgYWxzbyBjYWxjdWxhdGVkIHRoZSBudW1iZXIgb2Ygd29yZHMgaW4gZWFjaCBzZW50ZW5jZSBhcyAqc2VudGVuY2UgbGVuZ3RoKiAoYHdvcmQuY291bnRgKS4KCmBgYHtyLCBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFfQpzZW50ZW5jZS5saXN0PU5VTEwKZm9yKGkgaW4gMTpucm93KHNwZWVjaC5saXN0KSl7CiAgc2VudGVuY2VzPXNlbnRfZGV0ZWN0KHNwZWVjaC5saXN0JGZ1bGx0ZXh0W2ldLAogICAgICAgICAgICAgICAgICAgICAgICBlbmRtYXJrcyA9IGMoIj8iLCAiLiIsICIhIiwgInwiLCI7IikpCiAgaWYobGVuZ3RoKHNlbnRlbmNlcyk+MCl7CiAgICBlbW90aW9ucz1nZXRfbnJjX3NlbnRpbWVudChzZW50ZW5jZXMpCiAgICB3b3JkLmNvdW50PXdvcmRfY291bnQoc2VudGVuY2VzKQogICAgIyBjb2xuYW1lcyhlbW90aW9ucyk9cGFzdGUwKCJlbW8uIiwgY29sbmFtZXMoZW1vdGlvbnMpKQogICAgIyBpbiBjYXNlIHRoZSB3b3JkIGNvdW50cyBhcmUgemVyb3M/CiAgICBlbW90aW9ucz1kaWFnKDEvKHdvcmQuY291bnQrMC4wMSkpJSolYXMubWF0cml4KGVtb3Rpb25zKQogICAgc2VudGVuY2UubGlzdD1yYmluZChzZW50ZW5jZS5saXN0LCAKICAgICAgICAgICAgICAgICAgICAgICAgY2JpbmQoc3BlZWNoLmxpc3RbaSwtbmNvbChzcGVlY2gubGlzdCldLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICBzZW50ZW5jZXM9YXMuY2hhcmFjdGVyKHNlbnRlbmNlcyksIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICB3b3JkLmNvdW50LAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICBlbW90aW9ucywKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgc2VudC5pZD0xOmxlbmd0aChzZW50ZW5jZXMpCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICkKICAgICkKICB9Cn0KYGBgCgpTb21lIG5vbi1zZW50ZW5jZXMgZXhpc3QgaW4gcmF3IGRhdGEgZHVlIHRvIGVycm9uZW91cyBleHRyYSBlbmQtb2Ygc2VudGVuY2UgbWFya3MuIApgYGB7cn0Kc2VudGVuY2UubGlzdD0KICBzZW50ZW5jZS5saXN0JT4lCiAgZmlsdGVyKCFpcy5uYSh3b3JkLmNvdW50KSkgCgpgYGAKCiMgU3RlcCA1OiBEYXRhIGFuYWx5c2lzIC0tLSBsZW5ndGggb2Ygc2VudGVuY2VzCgpGb3Igc2ltcGxlciB2aXN1YWxpemF0aW9uLCB3ZSBjaG9zZSBhIHN1YnNldCBvZiBiZXR0ZXIga25vd24gcHJlc2lkZW50cyBvciBwcmVzaWRlbnRpYWwgY2FuZGlkYXRlcyBvbiB3aGljaCB0byBmb2N1cyBvdXIgYW5hbHlzaXMuIAoKYGBge3J9CgpzZWwuY29tcGFyaXNvbiA8LSBjKCkKZm9yKGkgaW4gMTpsZW5ndGgodW5pcXVlKHNwZWVjaC5saXN0JFByZXNpZGVudCkpKXsKICAgICAgICBzZWwuY29tcGFyaXNvbltpXSA8LSB1bmlxdWUoc3BlZWNoLmxpc3QkRmlsZSlbaV0KfQpzZWwuY29tcGFyaXNvbiA8LSBzZWwuY29tcGFyaXNvblstYyg1OTo2MSldCmBgYAoKIyMgT3ZlcnZpZXcgb2Ygc2VudGVuY2UgbGVuZ3RoIGRpc3RyaWJ1dGlvbiBieSBkaWZmZXJlbnQgdHlwZXMgb2Ygc3BlZWNoZXMuIAoKIyMjIE5vbWluYXRpb24gc3BlZWNoZXMgCgpGaXJzdCwgd2UgbG9vayBhdCAqbm9taW5hdGlvbiBhY2NlcHRhbmNlIHNwZWVjaGVzKiBhdCBtYWpvciBwYXJ0eSdzIG5hdGlvbmFsIGNvbnZlbnRpb25zLiBGb3IgcmVsZXZhbnQgdG8gVHJ1bXAncyBzcGVlY2hlcywgd2UgbGltaXQgb3VyIGF0dGVudGlvbiB0byBzcGVlY2hlcyBmb3IgdGhlIGZpcnN0IHRlcm1zIG9mIGZvcm1lciBVLlMuIHByZXNpZGVudHMuICBXZSBub3RpY2VkIHRoYXQgYSBudW1iZXIgb2YgcHJlc2lkZW50cyBoYXZlIHZlcnkgc2hvcnQgc2VudGVuY2VzIGluIHRoZWlyIG5vbWluYXRpb24gYWNjZXB0YW5jZSBzcGVlY2hlcy4gCgojIyMjIEZpcnN0IHRlcm0KCmBgYHtyLCBmaWcud2lkdGggPSA2LCBmaWcuaGVpZ2h0ID0gNn0KCnBhcihtYXI9Yyg0LCAxMSwgMiwgMikpCgojc2VsLmNvbXBhcmlzb249bGV2ZWxzKHNlbnRlbmNlLmxpc3QkRmlsZU9yZGVyZWQpCnNlbnRlbmNlLmxpc3Quc2VsPWZpbHRlcihzZW50ZW5jZS5saXN0LCAKICAgICAgICAgICAgICAgICAgICAgICAgRmlsZSA9PSAiRG9uYWxkSlRydW1wIikKc2VudGVuY2UubGlzdC5zZWwkdHlwZT1mYWN0b3Ioc2VudGVuY2UubGlzdC5zZWwkdHlwZSkKCnNlbnRlbmNlLmxpc3Quc2VsJHR5cGVPcmRlcmVkPXJlb3JkZXIoc2VudGVuY2UubGlzdC5zZWwkdHlwZSwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBzZW50ZW5jZS5saXN0LnNlbCR3b3JkLmNvdW50LCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIG1lYW4sIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgb3JkZXI9VCkKCmJlZXN3YXJtKHdvcmQuY291bnR+dHlwZU9yZGVyZWQsIAogICAgICAgICBkYXRhPXNlbnRlbmNlLmxpc3Quc2VsLAogICAgICAgICBob3Jpem9udGFsID0gVFJVRSwgCiAgICAgICAgIHBjaD0xNiwgY29sPWFscGhhKGJyZXdlci5wYWwoOSwgIlNldDEiKSwgMC42KSwgCiAgICAgICAgIGNleD0wLjU1LCBjZXguYXhpcz0wLjgsIGNleC5sYWI9MC44LAogICAgICAgICBzcGFjaW5nPTAuNS9ubGV2ZWxzKHNlbnRlbmNlLmxpc3Quc2VsJHR5cGVPcmRlcmVkKSwKICAgICAgICAgbGFzPTIsIHhsYWI9Ik51bWJlciBvZiB3b3JkcyBpbiBhIHNlbnRlbmNlLiIsIHlsYWI9IiIsCiAgICAgICAgIG1haW49IkRpZmZlcmVudCBzcGVlY2hlcyBvZiBEb25hbGRKVHJ1bXAiKQoKc2VudGVuY2UubGlzdCU+JQogIGZpbHRlcihGaWxlPT0iRG9uYWxkSlRydW1wIiwgCiAgICAgICAgIHR5cGU9PSJub21pbiIsIAogICAgICAgICB3b3JkLmNvdW50PD0zKSU+JQogIHNlbGVjdChzZW50ZW5jZXMpJT4lc2FtcGxlX24oMTApCgpzZW50ZW5jZS5saXN0JT4lCiAgZmlsdGVyKEZpbGU9PSJEb25hbGRKVHJ1bXAiLCAKICAgICAgICAgdHlwZT09ImluYXVnIiwgCiAgICAgICAgIHdvcmQuY291bnQ8PTMpJT4lCiAgc2VsZWN0KHNlbnRlbmNlcyklPiVzYW1wbGVfbig1KQoKc2VudGVuY2UubGlzdCU+JQogIGZpbHRlcihGaWxlPT0iRG9uYWxkSlRydW1wIiwgCiAgICAgICAgIHR5cGU9PSJzcGVlY2hlcyIsIAogICAgICAgICB3b3JkLmNvdW50PD0zKSU+JQogIHNlbGVjdChzZW50ZW5jZXMpJT4lc2FtcGxlX24oMTApCgpgYGAKCiMjIyMgU2Vjb25kIHRlcm0KCmBgYHtyfQoKY291bnRfdGVybSA8LSB0YWJsZShzcGVlY2gubGlzdFtzcGVlY2gubGlzdCR0eXBlID09ICJpbmF1ZyIsXSRGaWxlKQptdWx0aS50ZXJtLnNlbCA8LSBuYW1lcyh3aGljaChjb3VudF90ZXJtID4gMSkpCgoKZm9yKGkgaW4gMTpsZW5ndGgobXVsdGkudGVybS5zZWwpKXsKCiAgCiAgcG5nKHBhc3RlKCIuLi9vdXRwdXQvIiwgbXVsdGkudGVybS5zZWxbaV0sICJjb21wYXJlMTIucG5nIiwgc2VwPSIiKSwKICAgICAgd2lkdGg9MzAwLCBoZWlnaHQ9MzAwKQoKI3NlbC5jb21wYXJpc29uPWxldmVscyhzZW50ZW5jZS5saXN0JEZpbGVPcmRlcmVkKQpzZW50ZW5jZS5saXN0LnNlbD1maWx0ZXIoc2VudGVuY2UubGlzdCwgCiAgICAgICAgICAgICAgICAgICAgICAgIHR5cGU9PSJpbmF1ZyIsIEZpbGUlaW4lbXVsdGkudGVybS5zZWxbaV0pCnNlbnRlbmNlLmxpc3Quc2VsJFRlcm09ZmFjdG9yKHNlbnRlbmNlLmxpc3Quc2VsJFRlcm0pCgpzZW50ZW5jZS5saXN0LnNlbCR0ZXJtT3JkZXJlZD1yZW9yZGVyKHNlbnRlbmNlLmxpc3Quc2VsJFRlcm0sIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgc2VudGVuY2UubGlzdC5zZWwkd29yZC5jb3VudCwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBtZWFuLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIG9yZGVyPVQpCgpiZWVzd2FybSh3b3JkLmNvdW50fnRlcm1PcmRlcmVkLCAKICAgICAgICAgZGF0YT1zZW50ZW5jZS5saXN0LnNlbCwKICAgICAgICAgaG9yaXpvbnRhbCA9IFRSVUUsIAogICAgICAgICBwY2g9MTYsIGNvbD1hbHBoYShicmV3ZXIucGFsKDksICJTZXQxIiksIDAuNiksIAogICAgICAgICBjZXg9MC41NSwgY2V4LmF4aXM9MC44LCBjZXgubGFiPTAuOCwKICAgICAgICAgc3BhY2luZz0zL25sZXZlbHMoc2VudGVuY2UubGlzdC5zZWwkdGVybU9yZGVyZWQpLAogICAgICAgICBsYXM9MiwgeGxhYj0iTnVtYmVyIG9mIHdvcmRzIGluIGEgc2VudGVuY2UuIiwgeWxhYj0iIiwKICAgICAgICAgbWFpbj0iSW5hdWd1cmF0aW9uIHNwZWVjaGVzIG9mIG11bHRpLXRlcm0iKQogIGRldi5vZmYoKQogIAogIH0KYGBgCgpXaGF0IGFyZSB0aGVzZSBzaG9ydCBzZW50ZW5jZXM/CmBgYHtyfQpzZW50ZW5jZS5saXN0JT4lCiAgZmlsdGVyKEZpbGU9PSJEb25hbGRKVHJ1bXAiLCAKICAgICAgICAgdHlwZT09Im5vbWluIiwgCiAgICAgICAgIHdvcmQuY291bnQ8PTMpJT4lCiAgc2VsZWN0KHNlbnRlbmNlcyklPiVzYW1wbGVfbigxMCkKCgpzZW50ZW5jZS5saXN0JT4lCiAgZmlsdGVyKEZpbGU9PSJBbGJlcnRHb3JlLEpyIiwgCiAgICAgICAgIHR5cGU9PSJub21pbiIsIAogICAgICAgICB3b3JkLmNvdW50PD0zKSU+JQogIHNlbGVjdChzZW50ZW5jZXMpJT4lc2FtcGxlX24oMTApCgpzZW50ZW5jZS5saXN0JT4lCiAgZmlsdGVyKEZpbGU9PSJDbGludG9uIiwgCiAgICAgICAgIHR5cGU9PSJub21pbiIsIAogICAgICAgICB3b3JkLmNvdW50PD0zKSU+JQogIHNlbGVjdChzZW50ZW5jZXMpCgpzZW50ZW5jZS5saXN0JT4lCiAgZmlsdGVyKEZpbGU9PSJXaWxsaWFtSkNsaW50b24iLCAKICAgICAgICAgdHlwZT09Im5vbWluIiwgVGVybT09MSwKICAgICAgICAgd29yZC5jb3VudDw9MyklPiUKICBzZWxlY3Qoc2VudGVuY2VzKQpgYGAKCgojIyMgSW5hdWd1cmFsIHNwZWVjaGVzCgpXZSBub3RpY2UgdGhhdCB0aGUgc2VudGVuY2VzIGluIGluYXVndXJhbCBzcGVlY2hlcyBhcmUgbG9uZ2VyIHRoYW4gdGhvc2UgaW4gbm9taW5hdGlvbiBhY2NlcHRhbmNlIHNwZWVjaGVzLiAKCmBgYHtyLCBmaWcud2lkdGggPSA4LCBmaWcuaGVpZ2h0ID0gN30Kc2VudGVuY2UubGlzdC5zZWw9c2VudGVuY2UubGlzdCU+JWZpbHRlcih0eXBlPT0iaW5hdWciLCBGaWxlJWluJXNlbC5jb21wYXJpc29uLCBUZXJtPT0xKQpzZW50ZW5jZS5saXN0LnNlbCRGaWxlPWZhY3RvcihzZW50ZW5jZS5saXN0LnNlbCRGaWxlKQoKc2VudGVuY2UubGlzdC5zZWwkRmlsZU9yZGVyZWQ9cmVvcmRlcihzZW50ZW5jZS5saXN0LnNlbCRGaWxlLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHNlbnRlbmNlLmxpc3Quc2VsJHdvcmQuY291bnQsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbWVhbiwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBvcmRlcj1UKQpwYXIobWFyPWMoNCwgMTEsIDIsIDIpKQoKYmVlc3dhcm0od29yZC5jb3VudH5GaWxlT3JkZXJlZCwgCiAgICAgICAgIGRhdGE9c2VudGVuY2UubGlzdC5zZWwsCiAgICAgICAgIGhvcml6b250YWwgPSBUUlVFLAogICAgICAgICBwY2g9MTYsIGNvbD1hbHBoYShicmV3ZXIucGFsKDksICJTZXQxIiksIDAuNiksIAogICAgICAgICBjZXg9MC41NSwgY2V4LmF4aXM9MC44LCBjZXgubGFiPTAuOCwKICAgICAgICAgc3BhY2luZz01L25sZXZlbHMoc2VudGVuY2UubGlzdC5zZWwkRmlsZU9yZGVyZWQpLAogICAgICAgICBsYXM9MiwgeWxhYj0iIiwgeGxhYj0iTnVtYmVyIG9mIHdvcmRzIGluIGEgc2VudGVuY2UuIiwKICAgICAgICAgbWFpbj0iSW5hdWd1cmFsIFNwZWVjaGVzIikKYGBgCgpTaG9ydCBzZW50ZW5jZXMgaW4gaW5hdWd1cmFsIHNwZWVjaGVzLiAKYGBge3J9CnNlbnRlbmNlLmxpc3QlPiUKICBmaWx0ZXIoRmlsZT09IkJhcmFja09iYW1hIiwgCiAgICAgICAgIHR5cGU9PSJpbmF1ZyIsIAogICAgICAgICB3b3JkLmNvdW50PD0zKSU+JQogIHNlbGVjdChzZW50ZW5jZXMpCmBgYAoKCgpgYGB7cn0KdHJ1bXBfYmlncmFtcyA8LSBzcGVlY2gubGlzdFtzcGVlY2gubGlzdCRGaWxlID09ICJEb25hbGRKVHJ1bXAiLF0gJT4lCiAgdW5uZXN0X3Rva2VucyhiaWdyYW0sIGZ1bGx0ZXh0LCB0b2tlbiA9ICJuZ3JhbXMiLCBuID0gMikKCmJpZ3JhbXNfc2VwYXJhdGVkIDwtIHRydW1wX2JpZ3JhbXMgJT4lCiAgc2VwYXJhdGUoYmlncmFtLCBjKCJ3b3JkMSIsICJ3b3JkMiIpLCBzZXAgPSAiICIpCgpiaWdyYW1zX2ZpbHRlcmVkIDwtIGJpZ3JhbXNfc2VwYXJhdGVkICU+JQogIGZpbHRlcighd29yZDEgJWluJSBzdG9wX3dvcmRzJHdvcmQpICU+JQogIGZpbHRlcighd29yZDIgJWluJSBzdG9wX3dvcmRzJHdvcmQpCgpiaWdyYW1fY291bnRzIDwtIGJpZ3JhbXNfZmlsdGVyZWQgJT4lIAogIGNvdW50KHdvcmQxLCB3b3JkMiwgc29ydCA9IFRSVUUpCgpiaWdyYW1zX3VuaXRlZCA8LSBiaWdyYW1zX2ZpbHRlcmVkICU+JQogIHVuaXRlKGJpZ3JhbSwgd29yZDEsIHdvcmQyLCBzZXAgPSAiICIpCgoKYmlncmFtc191bml0ZWQgPC0gYmlncmFtc191bml0ZWQgJT4lCiAgY291bnQodHlwZSwgYmlncmFtKSAlPiUKICBhcnJhbmdlKGRlc2MobikpIAoKYmlncmFtc191bml0ZWQgPC0gYmlncmFtc191bml0ZWQgJT4lIAogIG11dGF0ZShiaWdyYW0gPSBmYWN0b3IoYmlncmFtLCBsZXZlbHMgPSByZXYodW5pcXVlKGJpZ3JhbSkpKSkgJT4lCiAgZ3JvdXBfYnkodHlwZSkgJT4lIAogIGFycmFuZ2UoZGVzYyhuKSklPiUKICB0b3Bfbig1KSAlPiUgCiAgdW5ncm91cAoKCmdncGxvdChiaWdyYW1zX3VuaXRlZCwgYWVzKGJpZ3JhbSwgbiwgZmlsbCA9IHR5cGUpKSArCiAgZ2VvbV9iYXIoc3RhdCA9ICJpZGVudGl0eSIsIHNob3cubGVnZW5kID0gRkFMU0UpICsKICBsYWJzKHggPSBOVUxMLCB5ID0gImNvdW50IG9mIHdvcmRzIikgKwogIGZhY2V0X3dyYXAofnR5cGUsIG5jb2wgPSAyLCBzY2FsZXMgPSAiZnJlZSIpICsKICBjb29yZF9mbGlwKCkKIyMjIyMjIyMjIyMjIyMjIyMjCgpgYGAKCgoKCgojIFN0ZXAgNTogRGF0YSBhbmFseXNpcyAtLS0gc2VudGltZW50IGFuYWxzaXMKCiMjIFNlbnRlbmNlIGxlbmd0aCB2YXJpYXRpb24gb3ZlciB0aGUgY291cnNlIG9mIHRoZSBzcGVlY2gsIHdpdGggZW1vdGlvbnMuIAoKSG93IG91ciBwcmVzaWRlbnRzIChvciBjYW5kaWRhdGVzKSBhbHRlcm5hdGUgYmV0d2VlbiBsb25nIGFuZCBzaG9ydCBzZW50ZW5jZXMgYW5kIGhvdyB0aGV5IHNoaWZ0IGJldHdlZW4gZGlmZmVyZW50IHNlbnRpbWVudHMgaW4gdGhlaXIgc3BlZWNoZXMuIEl0IGlzIGludGVyZXN0aW5nIHRvIG5vdGUgdGhhdCBzb21lIHByZXNpZGVudGlhbCBjYW5kaWRhdGVzJyBzcGVlY2ggYXJlIG1vcmUgY29sb3JmdWwgdGhhbiBvdGhlcnMuIEhlcmUgd2UgdXNlZCB0aGUgc2FtZSBjb2xvciB0aGVtZSBhcyBpbiB0aGUgbW92aWUgIkluc2lkZSBPdXQuIgoKIVtpbWFnZV0oaHR0cDovL3d3dy5zdGFmZm9yZHNjaG9vbHMubmV0L2Ntcy9saWIwMTEvVkEwMTgxODcyMy9DZW50cmljaXR5L0RvbWFpbi8zNTc0L2NoYXJhY3Rlcl9pY29uLnBuZykKCmBgYHtyLCBmaWcuaGVpZ2h0PTIuNSwgZmlnLndpZHRoPTJ9CnBhcihtZnJvdz1jKDQsMSksIG1hcj1jKDEsMCwyLDApLCBidHk9Im4iLCB4YXh0PSJuIiwgeWF4dD0ibiIsIGZvbnQubWFpbj0xKQoKZi5wbG90c2VudC5sZW4oSW4ubGlzdD1zZW50ZW5jZS5saXN0LCBJbkZpbGU9IkhpbGxhcnlDbGludG9uIiwgCiAgICAgICAgICAgICAgIEluVHlwZT0ibm9taW4iLCBJblRlcm09MSwgUHJlc2lkZW50PSJIaWxsYXJ5IENsaW50b24iKQoKZi5wbG90c2VudC5sZW4oSW4ubGlzdD1zZW50ZW5jZS5saXN0LCBJbkZpbGU9IkRvbmFsZEpUcnVtcCIsIAogICAgICAgICAgICAgICBJblR5cGU9Im5vbWluIiwgSW5UZXJtPTEsIFByZXNpZGVudD0iRG9uYWxkIFRydW1wIikKCmYucGxvdHNlbnQubGVuKEluLmxpc3Q9c2VudGVuY2UubGlzdCwgSW5GaWxlPSJCYXJhY2tPYmFtYSIsIAogICAgICAgICAgICAgICBJblR5cGU9Im5vbWluIiwgSW5UZXJtPTEsIFByZXNpZGVudD0iQmFyYWNrIE9iYW1hIikKCmYucGxvdHNlbnQubGVuKEluLmxpc3Q9c2VudGVuY2UubGlzdCwgSW5GaWxlPSJHZW9yZ2VXQnVzaCIsIAogICAgICAgICAgICAgICBJblR5cGU9Im5vbWluIiwgSW5UZXJtPTEsIFByZXNpZGVudD0iR2VvcmdlIFcuIEJ1c2giKQpgYGAKCiMjIyBXaGF0IGFyZSB0aGUgZW1vdGlvbmFsbHkgY2hhcmdlZCBzZW50ZW5jZXM/CgpgYGB7cn0KcHJpbnQoIkhpbGxhcnkgQ2xpbnRvbiIpCnNwZWVjaC5kZj10YmxfZGYoc2VudGVuY2UubGlzdCklPiUKICBmaWx0ZXIoRmlsZT09IkhpbGxhcnlDbGludG9uIiwgdHlwZT09Im5vbWluIiwgd29yZC5jb3VudD49NCklPiUKICBzZWxlY3Qoc2VudGVuY2VzLCBhbmdlcjp0cnVzdCkKc3BlZWNoLmRmPWFzLmRhdGEuZnJhbWUoc3BlZWNoLmRmKQphcy5jaGFyYWN0ZXIoc3BlZWNoLmRmJHNlbnRlbmNlc1thcHBseShzcGVlY2guZGZbLC0xXSwgMiwgd2hpY2gubWF4KV0pCgpwcmludCgiQmFyYWNrIE9iYW1hIikKc3BlZWNoLmRmPXRibF9kZihzZW50ZW5jZS5saXN0KSU+JQogIGZpbHRlcihGaWxlPT0iQmFyYWNrT2JhbWEiLCB0eXBlPT0ibm9taW4iLCBUZXJtPT0xLCB3b3JkLmNvdW50Pj01KSU+JQogIHNlbGVjdChzZW50ZW5jZXMsIGFuZ2VyOnRydXN0KQpzcGVlY2guZGY9YXMuZGF0YS5mcmFtZShzcGVlY2guZGYpCmFzLmNoYXJhY3RlcihzcGVlY2guZGYkc2VudGVuY2VzW2FwcGx5KHNwZWVjaC5kZlssLTFdLCAyLCB3aGljaC5tYXgpXSkKCnByaW50KCJHZW9yZ2UgVyBCdXNoIikKc3BlZWNoLmRmPXRibF9kZihzZW50ZW5jZS5saXN0KSU+JQogIGZpbHRlcihGaWxlPT0iR2VvcmdlV0J1c2giLCB0eXBlPT0ibm9taW4iLCBUZXJtPT0xLCB3b3JkLmNvdW50Pj00KSU+JQogIHNlbGVjdChzZW50ZW5jZXMsIGFuZ2VyOnRydXN0KQpzcGVlY2guZGY9YXMuZGF0YS5mcmFtZShzcGVlY2guZGYpCmFzLmNoYXJhY3RlcihzcGVlY2guZGYkc2VudGVuY2VzW2FwcGx5KHNwZWVjaC5kZlssLTFdLCAyLCB3aGljaC5tYXgpXSkKCnByaW50KCJEb25hbGQgVHJ1bXAiKQpzcGVlY2guZGY9dGJsX2RmKHNlbnRlbmNlLmxpc3QpJT4lCiAgZmlsdGVyKEZpbGU9PSJEb25hbGRKVHJ1bXAiLCB0eXBlPT0ibm9taW4iLCBUZXJtPT0xLCB3b3JkLmNvdW50Pj01KSU+JQogIHNlbGVjdChzZW50ZW5jZXMsIGFuZ2VyOnRydXN0KQpzcGVlY2guZGY9YXMuZGF0YS5mcmFtZShzcGVlY2guZGYpCmFzLmNoYXJhY3RlcihzcGVlY2guZGYkc2VudGVuY2VzW2FwcGx5KHNwZWVjaC5kZlssLTFdLCAyLCB3aGljaC5tYXgpXSkKCmBgYAoKCiMjIENsdXN0ZXJpbmcgb2YgZW1vdGlvbnMKYGBge3IsIGZpZy53aWR0aD0yLCBmaWcuaGVpZ2h0PTJ9CmhlYXRtYXAuMihjb3Ioc2VudGVuY2UubGlzdCU+JWZpbHRlcih0eXBlPT0iaW5hdWciKSU+JXNlbGVjdChhbmdlcjp0cnVzdCkpLCAKICAgICAgICAgIHNjYWxlID0gIm5vbmUiLCAKICAgICAgICAgIGNvbCA9IGJsdWVyZWQoMTAwKSwgLCBtYXJnaW49Yyg2LCA2KSwga2V5PUYsCiAgICAgICAgICB0cmFjZSA9ICJub25lIiwgZGVuc2l0eS5pbmZvID0gIm5vbmUiKQoKcGFyKG1hcj1jKDQsIDYsIDIsIDEpKQplbW8ubWVhbnM9Y29sTWVhbnMoc2VsZWN0KHNlbnRlbmNlLmxpc3QsIGFuZ2VyOnRydXN0KT4wLjAxKQpjb2wudXNlPWMoInJlZDIiLCAiZGFya2dvbGRlbnJvZDEiLCAKICAgICAgICAgICAgImNoYXJ0cmV1c2UzIiwgImJsdWV2aW9sZXQiLAogICAgICAgICAgICAiZGFya2dvbGRlbnJvZDIiLCAiZG9kZ2VyYmx1ZTMiLCAKICAgICAgICAgICAgImRhcmtnb2xkZW5yb2QxIiwgImRhcmtnb2xkZW5yb2QxIikKYmFycGxvdChlbW8ubWVhbnNbb3JkZXIoZW1vLm1lYW5zKV0sIGxhcz0yLCBjb2w9Y29sLnVzZVtvcmRlcihlbW8ubWVhbnMpXSwgaG9yaXo9VCwgbWFpbj0iSW5hdWd1cmFsIFNwZWVjaGVzIikKYGBgCgoKCg==